#pragma once

#include "Common.hpp"
#include "CentralCache.hpp"

class ThreadCache : protected PtrHelper
{
public:
	FreeList _freeList[NFREELIST];  // 默认开启 NFREELIST 个数量的 桶

	// 申请和释放内存对象
	void* Allocate(size_t bytes)
	{
		// 此函数只会选择一块合适的空间，每次只需要一块 bytes 大小的内存

		assert(bytes <= MAX_BYTES);  // < 256k
		size_t alignSize = SizeClass::RoundUp(bytes);
		size_t index = SizeClass::Index(bytes);

		if (!_freeList[index].empty())
		{
			// threadCache 有内存，先从 threadCache 获取
			return _freeList[index].Pop();
		}
		else
		{
			// threadCache 内存耗尽，从 CenterCache 获取

			return FetchFromCentralCache(index, alignSize);
			// Fetch  拿来
		}
	}

	void Deallocate(void* ptr, size_t byte_size)  // 给出归还的内存大小
	{
		// 归还内存
		assert(ptr);
		assert(byte_size <= MAX_BYTES);

		// 计算是哪一个桶下的
		size_t index = SizeClass::Index(byte_size);
		// 将内存归还到指定桶下
		_freeList[index].Push(ptr);

		// 当 _freeList 的链表的长度大于一次批量申请的内存时，就将内存返还给 central cache
		if (_freeList[index].Size() >=  _freeList[index].MaxSize()   )
		{
			ListTooLong(_freeList[index],byte_size );
		}
	}

	// 回收内存
	void ListTooLong(FreeList& list, size_t byte_size)
	{
		// 每次只回收其中的一部分，
		void* start = nullptr, * end = nullptr;
		list.PopRange(start, end, list.MaxSize());

		CentralCache::GetInstanse().ReleaseListToSpans(start, byte_size);

	}


	void* FetchFromCentralCache(size_t index, size_t byte_size)  // 获取大小为 byte_size 的内存块 ,放入下标为 index 的桶中，
	{
		// 开始慢反馈调节算法
		size_t batchNum = min(_freeList[index].MaxSize(), SizeClass::NumMoveSize(byte_size));
		// 1. 最开始不会一次向 Center Cache 要太多内存，太多会用不完
		//    size 越大，一次向 Center Cache 要的batchNum 数量就不会太多
		// 2. 且每次选取两者较小值，直到慢慢到达上限
		if (_freeList[index].MaxSize() == batchNum)
		{
			_freeList[index].MaxSize() += 2;  // 这里可以调节增长速度
		}

		void* start = nullptr, * end = nullptr;

		//actual_Num 实际获取到的数目
		size_t actual_Num =  CentralCache::GetInstanse().FetchRangeObj(start, end, batchNum, byte_size);  // 从中心缓存获取内存

		// 至少获取到1个
		assert(actual_Num >= 1);
		if (actual_Num == 1)
		{
			assert(start == end);
		}
		else
		{
			// 将多余的内存存入 ThreadCache 中，将第一块内存返回,存储 actual_Num - 1 个内存
			_freeList[index].PushRange(NextObj(start), end, actual_Num - 1);
		}

		return start;
	}
};

// 静态 TLS (线程局部存储)，这个变量在它所在的线程内是全局访问的，不能被其他线程访问到，
// 这样保证了线程的独立性

// 引入 TLS 是为了避免用锁，因为用锁效率不高
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;

// !!! 这里用static修饰，是因为头文件包含时，pTLSThreadCache会重复定义，就用static修饰